//---------------------------------------------------------------------------------
#include <stdio.h>
#include <QtDebug>
#include "CAStarCaisse.h"
#include "CAStar.h"
//---------------------------------------------------------------------------------
CAStarCaisse::CAStarCaisse(CMap* map) {
	origMap=map;
	this->map=map->clone();
}
//---------------------------------------------------------------------------------
bool CAStarCaisse::pushTo(int xSrc, int ySrc, int xDest, int yDest, QList<CMap::EAction> &actions) {
	bool fini=false;
	int xCur, yCur;
	int xP, yP;
	bool ok=false;
	int curValue=0;
	int i;
	
	this->xDest=xDest;
	this->yDest=yDest;
	map->getSize(w, h);
	size=w*h;
	buffer=new int[size];
	
	memset(buffer, -1, size*sizeof(int));
	
	buffer[ySrc*w+xSrc]=curValue;
	xCur=xSrc;
	yCur=ySrc;
	
	while(!fini) {
		QList<CMap::EAction> tmpActions;
		actions.clear();
		
		if(fillBuffer(curValue)) {
			int xT, yT, xF, yF;
			int iT;
			
			xT=xDest;
			yT=yDest;
			
			while(xT != xCur || yT != yCur) {
				iT=yT*w+xT;
				if(buffer[iT] == buffer[iT-w]+1) {
					yT--;
					tmpActions.insert(0, CMap::eaMoveDown);
				}else if(buffer[iT] == buffer[iT+w]+1) {
					yT++;
					tmpActions.insert(0, CMap::eaMoveUp);
				}else if(buffer[iT] == buffer[iT+1]+1) {
					xT++;
					tmpActions.insert(0, CMap::eaMoveLeft);
				}else if(buffer[iT] == buffer[iT-1]+1) {
					xT--;
					tmpActions.insert(0, CMap::eaMoveRight);
				}
			}
			
			QList<CMap::EAction> playerActions;
			bool playerFini=false;
			bool playerAuBout=false;
			i=0;
			
			map->getPlayerPos(xP, yP);
			while(!playerFini) {
				CAStar astar(map);
				playerActions.clear();
				switch(tmpActions.at(i)) {
					case CMap::eaMoveUp:
						xF=xT;
						yF=yT+1;
						if(!astar.goTo(xP, yP, xF, yF, playerActions)) {
							playerFini=true;
						}else {
							actions.append(playerActions);
							actions.append(CMap::eaPushUp);
							xP=xT;
							yP=yT;
							map->getCase(xT, yT)->setHasCaisse(false);
							map->getCase(xT, --yT)->setHasCaisse(true);
						}
						break;
					case CMap::eaMoveDown:
						xF=xT;
						yF=yT-1;
						
						if(!astar.goTo(xP, yP, xF, yF, playerActions)) {
							playerFini=true;
						}else {
							actions.append(playerActions);
							actions.append(CMap::eaPushDown);
							xP=xT;
							yP=yT;
							map->getCase(xT, yT)->setHasCaisse(false);
							map->getCase(xT, ++yT)->setHasCaisse(true);
						}
						break;
					case CMap::eaMoveRight:
						xF=xT-1;
						yF=yT;
						if(!astar.goTo(xP, yP, xF, yF, playerActions)) {
							playerFini=true;
						}else {
							actions.append(playerActions);
							actions.append(CMap::eaPushRight);
							xP=xT;
							yP=yT;
							map->getCase(xT, yT)->setHasCaisse(false);
							map->getCase(++xT, yT)->setHasCaisse(true);
						}	
						break;
					case CMap::eaMoveLeft:
						xF=xT+1;
						yF=yT;
						if(!astar.goTo(xP, yP, xF, yF, playerActions)) {
							playerFini=true;
						}else {
							actions.append(playerActions);
							actions.append(CMap::eaPushLeft);
							xP=xT;
							yP=yT;
							map->getCase(xT, yT)->setHasCaisse(false);
							map->getCase(--xT, yT)->setHasCaisse(true);
						}
						break;
					default:
						break;
				}
				
				i++;
				if(i == tmpActions.size() && !playerFini) {
					playerAuBout=true;
					playerFini=true;
				}
			}
			
			if(playerAuBout) {
				fini=true;
				ok=true;
			}else {
				delete map;
				map=origMap->clone();
				
				curValue=buffer[yT*w+xT];
				resetBuffer(curValue);
				buffer[yT*w+xT]=-2;
			}
		}else {
			fini=true;
		}
	}
	
	delete[] buffer;
	
	return ok;
}
//---------------------------------------------------------------------------------
bool CAStarCaisse::fillBuffer(int curValue) {
	int xC, yC;
	int i;
	bool isMove=false;
	int newValue=curValue+1;
	
	for(i=0;i<size;i++) {
		if(buffer[i] == curValue) {
			int xM, yM, xV, yV;
			int iM, iV;
			bool cond;
			CCase *c;
			xC=i%w;
			yC=i/w;
			
			xM=xC;
			yM=yC-1;
			iM=yM*w+xM;
			
			xV=xC;
			yV=yC+1;
			iV=yV*w+xV;
			
			c=map->getCase(xV, yV);
			cond=(c->getCaseType() == CCase::ectVide && !c->hasCaisse()) || buffer[iV] >=0;
			if(map->canMoveUp(xC, yC) && cond &&  buffer[iM] == -1) {
				buffer[iM]=newValue;
				if(yM == yDest && xM == xDest) {
					return true;
				}
				isMove=true;
			}
			
			xM=xC;
			yM=yC+1;
			iM=yM*w+xM;
			
			xV=xC;
			yV=yC-1;
			iV=yV*w+xV;
			
			c=map->getCase(xV, yV);
			cond=(c->getCaseType() == CCase::ectVide && !c->hasCaisse()) || buffer[iV] >=0;
			if(map->canMoveDown(xC, yC) && cond && buffer[iM] == -1) {
				buffer[iM]=newValue;
				if(yM == yDest && xM == xDest) {
					return true;
				}
				isMove=true;
			}
			
			xM=xC+1;
			yM=yC;
			iM=yM*w+xM;
			
			xV=xC-1;
			yV=yC;
			iV=yV*w+xV;
			
			c=map->getCase(xV, yV);
			cond=(c->getCaseType() == CCase::ectVide && !c->hasCaisse()) || buffer[iV] >=0;
			if(map->canMoveRight(xC, yC) && cond && buffer[iM] == -1) {
				buffer[iM]=newValue;
				if(yM == yDest && xM == xDest) {
					return true;
				}
				isMove=true;
			}
			
			xM=xC-1;
			yM=yC;
			iM=yM*w+xM;
			
			xV=xC+1;
			yV=yC;
			iV=yV*w+xV;
			
			c=map->getCase(xV, yV);
			cond=(c->getCaseType() == CCase::ectVide && !c->hasCaisse()) || buffer[iV] >=0;
			if(map->canMoveLeft(xC, yC) && cond && buffer[iM] == -1) {
				buffer[iM]=newValue;
				if(yM == yDest && xM == xDest) {
					return true;
				}
				isMove=true;
			}
		}
	}
	
	if(isMove) {
		return fillBuffer(newValue);
	}
	
	return false;
}
//---------------------------------------------------------------------------------
void CAStarCaisse::resetBuffer(int value) {
	for(int i=0;i<size;i++) {
		if(buffer[i] > value) {
			buffer[i]=-1;
		}
	}
}
//---------------------------------------------------------------------------------